package com.emin.platform.productCenter.multidatasource;

import com.emin.platform.productCenter.util.PltThreadLocalUtil;
import org.apache.log4j.Logger;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.orm.hibernate5.SessionFactoryUtils;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 动态创建数据源
 *
 * @author Jack
 * @version V1.0
 * @Description: 此类用于动态创建数据源，创建后数据源保存在dsMap中
 * @date 2017/10/24 14:42
 */
public class MultiDataSource implements DataSource {
    // 数据源保存map
    private static Map<Long, DataSource> dsMap;
    private final ReentrantLock lock = new ReentrantLock();


    // 默认的数据源类型
    private static final String DEFAULT_DATASOUCE_TYPE = "com.alibaba.druid.pool.DruidDataSource";
    private Logger logger = Logger.getLogger(MultiDataSource.class);


    // 单例
    private static MultiDataSource dataSource = new MultiDataSource();

    private MultiDataSource() {
        dsMap = new HashMap();
    }

    public static MultiDataSource getInstance() {
        return dataSource;
    }

    public static void setDsMap(Map<Long, DataSource> dsMap) {
        dsMap = dsMap;
    }

    public Set<Entry<Long, DataSource>> getDataSourceSet() {
        return dsMap.entrySet();
    }

    public Set<Long> getDataSourceKeySet() {
        return dsMap.keySet();
    }


    /**
     * 根据公司ID获取数据源
     *
     * @return DataSource 数据源
     * @throws
     * @Description 首先从dsMap中取，没有则创建
     * @author Jack
     * @date 2017/10/24
     */
    public DataSource getDataSource() {
        Long companyId = this.getCompanyId();
        this.logger.info("Get Data Source...");
        DataSource dataSource = (DataSource) dsMap.get(companyId);
        if (dataSource != null) {
            return dataSource;
        } else {
            return this.buildDataSourceByCompanyId();
        }
    }

    /**
     * 创建数据源
     *
     * @return DataSource 数据源
     * @throws
     * @Description
     * @author Jack
     * @date 2017/10/24
     */
    private DataSource buildDataSourceByCompanyId() {
        this.logger.info("Build Data Source...");
        Long companyId = this.getCompanyId();
        // 根据HTTP接口获取数据源配置信息
        Map<String, Object> dataSourceMap = this.getDataSourceConfigure();
        DataSource dataSource = this.buildDataSource(dataSourceMap);
  /*          JSONObject params = new JSONObject();
            params.put("id", companyId);
            JSONObject re = HttpUtil.httpPost(host, port, "http://localhost:8762/db/get",
                    params, "cjk");
            dbUrl = "jdbc:postgresql://" + re.getString("dbHost") + ":" + re.getString
                    ("dbPort");
            dataSource = this.bulidDataSource(dbUrl, re.getString("dbUser"), re.getString
                    ("dbPassword"), re.getString("dbName"));*/
        /*else {
            // 给个默认值
            companyId = 0L;
            dbUrl = "jdbc:postgresql://localhost:5432";
            dataSource = this.bulidDataSource(dbUrl, "postgres", "123456", "cjk");
        }*/
        if (companyId != null) {
            dsMap.put(companyId, dataSource);
        }
        return dataSource;
    }

    private Map<String, Object> getDataSourceConfigure() {
        Long companyId = this.getCompanyId();
        Map<String, Object> configureMap = new HashMap<>();
        if (companyId == null) {
            configureMap.put("url", "jdbc:postgresql://192.168.0.200:5432/cjk");
            configureMap.put("username", "postgres");
            configureMap.put("password", "1");
            configureMap.put("driver-class-name", "org.postgresql.Driver");
        } else {
            if (companyId.compareTo(new Long(1L)) == 0) {
                configureMap.put("url", "jdbc:postgresql://192.168.0.200:5432/cjk");
                configureMap.put("username", "postgres");
                configureMap.put("password", "1");
                configureMap.put("driver-class-name", "org.postgresql.Driver");
            } else {
                configureMap.put("url", "jdbc:postgresql://localhost:5432/cjk");
                configureMap.put("username", "postgres");
                configureMap.put("password", "123456");
                configureMap.put("driver-class-name", "org.postgresql.Driver");
            }

        }
        return configureMap;
    }

    /*private DataSource bulidDataSource(String dbUrl, String userName, String password, String
            dbName) {
        this.logger.info("driverClass:" + driverClass + ":" + dbUrl + "/" + dbName);
        try {
            DataSource ds = (DataSource) Class.forName(poolClass).newInstance();
            if (ds == null) {
                this.logger.error("Pool initial failed");
            }
            ComboPooledDataSource cpds = (ComboPooledDataSource) ds;
            cpds.setDriverClass(driverClass);
            cpds.setJdbcUrl(dbUrl + "/" + dbName);
            cpds.setUser(userName);
            cpds.setPassword(password);
            cpds.setMinPoolSize(Integer.parseInt(minPoolSize));
            cpds.setMaxPoolSize(Integer.parseInt(maxPoolSize));
            cpds.setMaxIdleTime(Integer.parseInt(maxIdleTime));
            return cpds;
        } catch (Exception var7) {
            var7.printStackTrace();
            return null;
        }
    }*/

    private DataSource buildDataSource(Map<String, Object> dataSourceMap) {
        Object type = dataSourceMap.get("type");
        type = type == null ? DEFAULT_DATASOUCE_TYPE : type;
        try {
            Class<? extends DataSource> dataSourceType = (Class<? extends DataSource>) Class
                    .forName(type.toString());
            String driverClassName = dataSourceMap.get("driver-class-name").toString();
            String url = dataSourceMap.get("url").toString();
            String username = dataSourceMap.get("username").toString();
            String password = dataSourceMap.get("password").toString();
            return DataSourceBuilder.create().type(dataSourceType).driverClassName
                    (driverClassName).url(url)
                    .username(username).password(password).build();
        } catch (ClassNotFoundException e) {
            logger.error("创建数据源失败：{}", e);
            return null;
        }
    }

    private Long getCompanyId() {
        return PltThreadLocalUtil.getCompanyId();
    }

    private Connection getCurrentEminConnection() {
        this.lock.tryLock();
        try {
            DataSource ds = this.getDataSource();
            Connection conn = ds.getConnection();
            System.out.println("Conn in Company:" + conn.getMetaData().getURL());
            Connection var3 = conn;
            return var3;
        } catch (Exception var7) {
            this.logger.info("Get connection failed:" + var7.getMessage());
            var7.printStackTrace();
        } finally {
            if (this.lock.isHeldByCurrentThread()) {
                this.lock.unlock();
            }

        }
        return null;
    }

    public void releaseResource(Connection conn, Statement statement, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException var7) {
                var7.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException var6) {
                var6.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException var5) {
                var5.printStackTrace();
            }
        }

    }


    public void removeDataSource(Long dataSourceKey) {
        if (dataSourceKey != null) {
            dsMap.remove(dataSourceKey);
        }
    }

    public Connection getConnection() throws SQLException {
        return this.getCurrentEminConnection();
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return this.getDataSource().getConnection();
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        return this.getDataSource().unwrap(iface);
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return this.getDataSource().isWrapperFor(iface);
    }

    public PrintWriter getLogWriter() throws SQLException {
        return this.getDataSource().getLogWriter();
    }

    public void setLogWriter(PrintWriter out) throws SQLException {
        this.getDataSource().setLogWriter(out);
    }

    public void setLoginTimeout(int seconds) throws SQLException {
        this.getDataSource().setLoginTimeout(seconds);
    }

    public int getLoginTimeout() throws SQLException {
        return this.getDataSource().getLoginTimeout();
    }

    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return this.getDataSource().getParentLogger();
    }

    /*static {
        ClassPathResource resource = new ClassPathResource("application.yml");
        try {
            Properties props = PropertiesLoaderUtils.loadProperties(resource);
            host = props.getProperty("host").trim();
            port = Integer.valueOf(props.getProperty("port").trim());
            driverClass = props.getProperty("driverClass").trim();
            minPoolSize = props.getProperty("minPoolSize").trim();
            maxPoolSize = props.getProperty("maxPoolSize").trim();
            maxIdleTime = props.getProperty("maxIdleTime").trim();
            poolClass = props.getProperty("poolClass").trim();
        } catch (IOException var2) {
            var2.printStackTrace();
        } catch (Exception var3) {
            var3.printStackTrace();
        }
    }*/


    public DataSource getLocalDataSourceByConnectionProvider(SessionFactoryImpl sessionFactory) {
        return SessionFactoryUtils.getDataSource(sessionFactory);
    }
}
